Skip to content

[BREAKING] Drop per-edge index wrappers + non-OpSum ttn ctors; rename scale -> scale_tensors#355

Merged
mtfishman merged 5 commits into
mainfrom
mf/drop-per-edge-wrappers
May 7, 2026
Merged

[BREAKING] Drop per-edge index wrappers + non-OpSum ttn ctors; rename scale -> scale_tensors#355
mtfishman merged 5 commits into
mainfrom
mf/drop-per-edge-wrappers

Conversation

@mtfishman
Copy link
Copy Markdown
Member

@mtfishman mtfishman commented May 7, 2026

Summary

Phase 3 ITensorNetworks.jl cleanup, consolidating multiple breaking changes into a single 0.20.0 -> 0.21.0 bump rather than churning multiple breaking releases.

1. Drop per-edge commoninds/uniqueinds/hascommoninds wrappers

Removes the deprecated/experimental AbstractITensorNetwork- and AbstractIndsNetwork-side per-edge index wrappers in favor of the kept linkinds(tn, edge) accessor and explicit intersect/setdiff calls on raw inds(...) at call sites.

Deletions:

  • commoninds(tn::AbstractITensorNetwork, edge) — was a thin alias for commoninds(tn[src(e)], tn[dst(e)]). The kept linkinds(tn, edge) returns the same thing.
  • uniqueinds(tn::AbstractITensorNetwork, edge::AbstractEdge|Pair).
  • hascommoninds(tn::AbstractITensorNetwork, edge::AbstractEdge|Pair).
  • uniqueinds(is::AbstractIndsNetwork, edge::AbstractEdge|Pair) — walked the IndsNetwork to collect site inds at src(edge) plus link inds on all other edges incident to src(edge). The single internal caller (in ttn(::ITensor, ::IndsNetwork)) inlines a semantically-equivalent rewrite using the already-built tn.

Reimplementation:
linkinds(tn::AbstractITensorNetwork, edge) is kept (parallel to the also-kept siteinds(tn, vertex)) and reimplemented natively as intersect(inds(tn[src(e)]), inds(tn[dst(e)])) instead of delegating to the deleted commoninds(tn, edge) wrapper.

Call site rewrites:

  • Graphs.weights(::AbstractITensorNetwork)commoninds(graph, e) -> linkinds(graph, e).
  • IndsNetwork(tn) and 1-arg linkinds(tn)commoninds(tn, e) -> linkinds(tn, e).
  • fix_edges!, union(tn1, tn2)hascommoninds(tn, e) -> !isempty(linkinds(tn, e)).
  • insert_linkinds!hascommoninds(tn, e) -> isempty(linkinds(tn, e)).
  • LinearAlgebra.svd/qr/factorize, gauge_edge, _truncate_edge (5 sites) — uniqueinds(tn, edge) -> setdiff(inds(tn[src(edge)]), inds(tn[dst(edge)])).
  • ttn(::ITensor, ::IndsNetwork) loop — uniqueinds(is, e) -> setdiff(inds(a), inds(tn[dst(e)])). Equivalent given the post-order-DFS invariant: tn[dst(e)] retains its original inds (parent vertices aren't processed as src until after their subtree), so the only inds shared with the running accumulator a are the link inds on edge e.
  • 2 sites in test/test_itensornetwork.jl "Index access" testset.

2. Drop non-OpSum ttn constructors

Removes 6 thin wrappers in src/treetensornetworks/opsum_to_ttn/opsum_to_ttn.jl that all boil down to ttn(OpSum{...}() + o, s; kwargs...):

ttn(o::Op, s::IndsNetwork; kwargs...)
ttn(o::Scaled{C, Op}, s::IndsNetwork; kwargs...)
ttn(o::Sum{Op}, s::IndsNetwork; kwargs...)
ttn(o::Prod{Op}, s::IndsNetwork; kwargs...)
ttn(o::Scaled{C, Prod{Op}}, s::IndsNetwork; kwargs...)
ttn(o::Sum{Scaled{C, Op}}, s::IndsNetwork; kwargs...)

Zero callers in src/ or test/. Users with Op-shaped operators can wrap into an OpSum themselves and call ttn(opsum, sites). The Op-adjacent forms can come back later through Lukas's symbolic operator system if needed.

3. Rename scale/scale! -> scale_tensors/scale_tensors!

The interface-doc comment for these has been flagging them as too generically named — they collide with broader scaling semantics, and the intent is "scale individual vertex tensors by per-vertex weights, leaving the graph alone." The rename clarifies that.

Mechanical: 4 internal callers (src/normalize.jl x2, src/caches/abstractbeliefpropagationcache.jl x2), plus the definitions in src/abstractitensornetwork.jl and the entry in docs/src/interface_methods.md. No tests reference the old names.

4. Doc + import housekeeping

  • Removes the corresponding entries from docs/src/deprecated_methods.md and docs/src/experimental_methods.md.
  • Drops dead combine_linkinds/linkinds_combiners imports from test/test_belief_propagation.jl (imported but never used).
  • Drops dead commoninds/uniqueinds/hascommoninds-related imports from src/abstractitensornetwork.jl and src/abstractindsnetwork.jl.
  • Adds explicit using ITensors: hascommoninds, uniqueinds imports to several files (apply.jl, solvers/subspace/densitymatrix.jl, solvers/applyexp.jl, treetensornetworks/projttns/projttn.jl, projouterprodttn.jl) that were inheriting these names via transitive scope from abstractitensornetwork.jl's top-level imports. The transitive dependency is now explicit.

Version bump

0.20.0 -> 0.21.0 (breaking — public methods removed/renamed).

Release notes:

Breaking. Removed public methods: ITensors.commoninds(tn::AbstractITensorNetwork, edge), ITensors.uniqueinds(tn::AbstractITensorNetwork, edge::AbstractEdge|Pair), ITensors.hascommoninds(tn::AbstractITensorNetwork, edge::AbstractEdge|Pair), ITensors.uniqueinds(is::AbstractIndsNetwork, edge::AbstractEdge|Pair), and 6 non-OpSum ttn constructors (ttn(::Op, ...), ttn(::Scaled{C, Op}, ...), ttn(::Sum{Op}, ...), ttn(::Prod{Op}, ...), ttn(::Scaled{C, Prod{Op}}, ...), ttn(::Sum{Scaled{C, Op}}, ...)). Renamed scale/scale! to scale_tensors/scale_tensors!. The linkinds(tn::AbstractITensorNetwork, edge) accessor is preserved with a re-implemented body using intersect. For unique-side and shared-side queries, use explicit setdiff(inds(tn[src(e)]), inds(tn[dst(e)])) and intersect(inds(tn[src(e)]), inds(tn[dst(e)])) (or linkinds(tn, e) for the shared case). For Op/Scaled/Sum/Prod operator inputs to ttn, wrap into an OpSum first.

mtfishman and others added 2 commits May 7, 2026 15:15
…appers

Followup to #354. Removes the deprecated/experimental `AbstractITensorNetwork`-
and `AbstractIndsNetwork`-side per-edge index wrappers in favor of the kept
`linkinds(tn, edge)` accessor and explicit `intersect`/`setdiff` calls on
raw `inds(...)` at call sites.

## Deletions

- `commoninds(tn::AbstractITensorNetwork, edge)` — was a thin alias for
  `commoninds(tn[src(e)], tn[dst(e)])`. The kept `linkinds(tn, edge)`
  accessor returns the same thing. The two were aliases anyway.
- `uniqueinds(tn::AbstractITensorNetwork, edge::AbstractEdge)` and the
  `Pair` overload — was `uniqueinds(tn[src(e)], tn[dst(e)])`.
- `hascommoninds(tn::AbstractITensorNetwork, edge::AbstractEdge|Pair)` —
  was `hascommoninds(tn[src(e)], tn[dst(e)])`.
- `uniqueinds(is::AbstractIndsNetwork, edge::AbstractEdge|Pair)` — walked
  the IndsNetwork to collect site inds at `src(edge)` plus link inds on all
  other edges incident to `src(edge)`. The single internal caller (in
  `ttn(::ITensor, ::IndsNetwork)`) inlines a semantically-equivalent rewrite
  using the already-built `tn`.

## Reimplementation

`linkinds(tn::AbstractITensorNetwork, edge)` is kept (parallel to the
also-kept `siteinds(tn, vertex)`) and reimplemented natively as
`intersect(inds(tn[src(e)]), inds(tn[dst(e)]))` instead of delegating to
the deleted `commoninds(tn, edge)` wrapper.

## Call site rewrites

- `weights(::AbstractITensorNetwork)` (`abstractitensornetwork.jl:34`) —
  `commoninds(graph, e)` → `linkinds(graph, e)`. The misleadingly-named
  `graph` argument is actually an `AbstractITensorNetwork`; this site
  was relying on the deleted wrapper.
- `IndsNetwork(tn)` and 1-arg `linkinds(tn)` (`abstractitensornetwork.jl`
  lines 249, 267) — `commoninds(tn, e)` → `linkinds(tn, e)`.
- `fix_edges!`, `union(tn1, tn2)` (lines 138, 183) — `hascommoninds(tn, e)`
  → `!isempty(linkinds(tn, e))`.
- `insert_linkinds` (line 848) — `!hascommoninds(tn, e)` →
  `isempty(linkinds(tn, e))`.
- `LinearAlgebra.svd`/`qr`/`factorize`, `gauge_edge`, `_truncate_edge`
  (5 sites) — `uniqueinds(tn, edge)` →
  `setdiff(inds(tn[src(edge)]), inds(tn[dst(edge)]))`.
- `ttn(::ITensor, ::IndsNetwork)` loop in `treetensornetwork.jl:233` —
  `uniqueinds(is, e)` → `setdiff(inds(a), inds(tn[dst(e)]))`. Equivalent
  given the post-order-DFS invariant: `tn[dst(e)]` retains its original
  inds (parent vertices aren't processed as `src` until after their
  subtree), so the only inds shared with the running accumulator `a` are
  the link inds on edge `e`.
- 2 sites in `test/test_itensornetwork.jl` "Index access" testset.

## Doc sync

Removes the corresponding entries from `docs/src/deprecated_methods.md`
(per-edge `commoninds` / `uniqueinds` / `linkinds` block) and
`docs/src/experimental_methods.md` (IndsNetwork-side `uniqueinds`).
Drops dead imports (`hascommoninds`, `unioninds`, `uniqueinds` from
`abstractitensornetwork.jl`; `ITensors`, `IndexSet`, `uniqueinds` from
`abstractindsnetwork.jl`; `hascommoninds`, `uniqueinds` from
`test/test_itensornetwork.jl`).

## Version bump

`0.20.0 → 0.21.0` (breaking — public methods removed). The pin in
`test/`, `examples/`, and `docs/` `Project.toml` is bumped from `"0.20"`
to `"0.21"` to match.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The previous commit dropped `ITensors` (the module name itself) from
this file's import alongside the now-unused `IndexSet` and `uniqueinds`.
That broke a transitive dependency: many other files in `src/` use
qualified `ITensors.foo(...)` syntax (e.g. `ITensors.ITensor(o, s)` in
`src/opsum.jl:7`) but only specific names from ITensors, not the module
itself. They were relying on `abstractindsnetwork.jl` (loaded early in
the include chain) to bring `ITensors` into the module's namespace.

Local tests passed because Revise's in-memory module had the symbol
already, but cold precompilation in CI failed at `src/opsum.jl:7` with
`UndefVarError: ITensors not defined in ITensorNetworks`.

Minimal fix: re-add `ITensors` (module name) to this file's import.
The proper long-term fix is for each file that uses `ITensors.foo`
qualification to import the module itself, but that's a separate
refactor.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@codecov
Copy link
Copy Markdown

codecov Bot commented May 7, 2026

Codecov Report

❌ Patch coverage is 84.00000% with 4 lines in your changes missing coverage. Please review.
✅ Project coverage is 74.60%. Comparing base (fb1d04f) to head (ea6ff8e).

Files with missing lines Patch % Lines
src/abstractitensornetwork.jl 80.00% 4 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #355      +/-   ##
==========================================
+ Coverage   74.40%   74.60%   +0.20%     
==========================================
  Files          64       64              
  Lines        3133     3103      -30     
==========================================
- Hits         2331     2315      -16     
+ Misses        802      788      -14     
Flag Coverage Δ
docs 48.20% <58.33%> (+0.08%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

mtfishman and others added 3 commits May 7, 2026 15:43
…ope was lost

Continuing the precompile fix from the previous commit. Several files
across `src/` call bare `hascommoninds(t1::ITensor, t2::ITensor)` or
`uniqueinds(t1::ITensor, t2::ITensor)` (both ITensors functions on
ITensor pairs — these are legitimate, not the deleted network+edge
wrappers) but don't import those names themselves. They were relying on
`abstractitensornetwork.jl`'s top-level `using ITensors: ..., hascommoninds,
unioninds, uniqueinds, ...` to bring those names into the
`ITensorNetworks` module namespace via transitive scope.

The wrapper deletion in this PR removed those imports from
`abstractitensornetwork.jl` (since the wrappers it defined were the only
local users), which in turn breaks the transitive scope downstream files
were depending on. Cold precompile fails; Revise's incremental updates
mask the problem locally.

Files updated (each gets only the names it actually uses):

- `src/apply.jl` — adds `hascommoninds` to the existing
  `using ITensors: ...` line.
- `src/solvers/subspace/densitymatrix.jl` — adds
  `using ITensors: hascommoninds, uniqueinds`.
- `src/solvers/applyexp.jl` — adds `using ITensors: uniqueinds`.
- `src/treetensornetworks/projttns/projttn.jl` — adds `hascommoninds`
  to the existing `using ITensors: ITensor` line.
- `src/treetensornetworks/projttns/projouterprodttn.jl` — same.

`src/solvers/subspace/ortho_subspace.jl` already does bare
`using ITensors`, which brings everything into scope, so it's unaffected.

These imports made the transitive dependency that already existed
explicit. No semantic changes.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…scale_tensors`/`scale_tensors!`

Folding two more breaking changes into this PR to consolidate the
0.20 -> 0.21 bump rather than churning multiple breaking releases.

## Drop non-`OpSum` `ttn` constructors

Removes 6 thin wrappers in `src/treetensornetworks/opsum_to_ttn/opsum_to_ttn.jl`
that all boil down to `ttn(OpSum{...}() + o, s; kwargs...)`:

```julia
ttn(o::Op, s::IndsNetwork; kwargs...)
ttn(o::Scaled{C, Op}, s::IndsNetwork; kwargs...)
ttn(o::Sum{Op}, s::IndsNetwork; kwargs...)
ttn(o::Prod{Op}, s::IndsNetwork; kwargs...)
ttn(o::Scaled{C, Prod{Op}}, s::IndsNetwork; kwargs...)
ttn(o::Sum{Scaled{C, Op}}, s::IndsNetwork; kwargs...)
```

Zero callers in `src/` or `test/` (only the function definitions
themselves matched). Users with `Op`-shaped operators can wrap into an
`OpSum` themselves and call `ttn(opsum, sites)`. The `Op`-adjacent forms
can come back later through Lukas's symbolic operator system if needed.
Removed the corresponding entry from `docs/src/deprecated_methods.md`.

## Rename `scale` / `scale!` -> `scale_tensors` / `scale_tensors!`

The interface-doc comment for these has been flagging them as too
generically named — they collide with broader scaling semantics, and the
intent is "scale individual vertex tensors by per-vertex weights, leaving
the graph alone." Rename clarifies that.

Mechanical: 4 internal callers (`src/normalize.jl` x2,
`src/caches/abstractbeliefpropagationcache.jl` x2), plus the definitions
in `src/abstractitensornetwork.jl` and the entry in
`docs/src/interface_methods.md`. No tests reference the old names.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
These are imported on lines 4-5 of `test/test_belief_propagation.jl`
but never actually called in the test body. The functions still exist
in `src/` (used by `add(tn1, tn2)` to collapse multi-edges) — this
just removes the unused test imports.

The longer-term plan to drop `combine_linkinds`/`linkinds_combiners`
themselves is now noted in the `insert_linkinds` redesign followup
(item #11 in `tn_stack_consolidation/Overview.md`): a proper
`edge_factorization`/`add_edge!` interface subsumes "collapse a
multi-edge into a single canonical bond" as a special case.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@mtfishman mtfishman changed the title [BREAKING] Drop per-edge commoninds/uniqueinds/hascommoninds wrappers [BREAKING] Drop per-edge index wrappers + non-OpSum ttn ctors; rename scale -> scale_tensors May 7, 2026
@mtfishman mtfishman enabled auto-merge (squash) May 7, 2026 20:00
@mtfishman mtfishman merged commit e1e7ed9 into main May 7, 2026
18 checks passed
@mtfishman mtfishman deleted the mf/drop-per-edge-wrappers branch May 7, 2026 20:21
mtfishman added a commit that referenced this pull request May 9, 2026
…neric ctors (#356)

## Summary

Continues the post-#355 cleanup. Drops the rest of the higher-level
`ITensorNetwork` and `ttn` / `mps` constructor surface that wrapped the
old
`IndsNetwork`-based code paths, plus the `ttn(::OpSum, ::IndsNetwork)`
and
`ttn(::ITensor, ::IndsNetwork)` dispatches (canonical name is now
`TreeTensorNetwork`; `mpo` continues to dispatch through to it).
Surviving
construction surface: `ITensorNetwork(tensors)`,
`ITensorNetwork(tensors, graph)`, and
`TreeTensorNetwork(::OpSum / ::ITensor / ::ITensorNetwork, ...)`.

Adds `factorize_edge!` and `Graphs.add_edge!` on
`AbstractITensorNetwork`.
Internal callers that built `ITensorNetwork(::IndsNetwork)` placeholders
now
build `ITensorNetwork(tensors, graph)` directly. Folds in two bug fixes
(missing `trivial_space` import; `_siteinds` returning `Tuple` instead
of
`Vector{Index}`).

`test/utils.jl` substantially simplified (~261 → ~75 effective lines):
surviving helpers are `random_tensornetwork`, `productstate`, and
`ModelHamiltonians`; `Distributions` dep dropped. Docs updated to the
canonical construction. `Project.toml` bumped 0.21 → 0.22.0-DEV
(breaking;
accumulator pattern — a strip-suffix PR will release 0.22.0 once v0.22
cleanups are batched).

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant